Code Caveats

For Portability

Groovy allows you to access the private fields of a class. Accessing private fields in your Groovy scripts can lead to problems during upgrades if these fields are renamed across N4 releases. So, we recommend that you first look for a public accessor for a Groovy class and only if you don't find any should you resort to using a private field (if at all).

 

Avoid Subtle Coding Caveats

Condition Checks

Please do explicit check for null as below

Double var = 0.0

if (var != null) {

// You should enter here.

}

Do the below only if you understand the side effects.

Double var = 0.0

// groovy treats zero value as false and you won't enter the condition

if (var) {

// you won't enter here.

}

 

Anonymous inner classes will not capture loop variables in an expected manner

There is a subtle difference in the way anonymous inner classes work in Java and groovy. In the example below, we create an anonymous Runnable.  The value of 'i' is obtained from the loop variable. In Java, the value is captured at the time the anonymous class is instantiated (for example, new Runnable() {}). Thus, Java uses the current value of the loop variable (when the object is created), and creates one Runnable for each value of i 1->1000. Groovy, for whatever reason, does not read the value of 'i' until the time when the Runnable executes. It does not capture the value of the current loop variable when the Runnable is created. Thus, in Groovy, it is possible (probable) that the runnable tasks will see duplicate values for i. The test below passes in Java and fails in Groovy.

  public void testAnonymousClassWithLoopVariable() throws Exception {

    final List<Integer> list = new ArrayList<>();

    final int size = 1000;

    for(int i = 0; i < size; i++) {

      list.add(i);

    }

    final AtomicInteger counter = new AtomicInteger();

    final AtomicReference<String> failureMessage = new AtomicReference<>(null);

    ExecutorService e = Executors.newSingleThreadExecutor(new NamedThreadFactory(Executors.defaultThreadFactory(), "loopTest"));

    for(final Integer i : list) {

      e.submit(new Runnable() {

        @Override

        public void run() {

          if(!counter.compareAndSet(i, i+1)) {

            failureMessage.set("failed at " + i);

          }

        }

      });

    }

    e.shutdown();

    e.awaitTermination(5, TimeUnit.MINUTES);

    assertNull(failureMessage.get());

  }